#include "openssl_study.h"

static int print_provider(OSSL_PROVIDER *provider, void *arg)
{
    (void)arg;
    const char *name = OSSL_PROVIDER_get0_name(provider);
    printf("PROVIDER: %s\n", name ? name : "unknown");
    return 0;
}

static void print_cipher(EVP_CIPHER *cipher, void *arg)
{
    (void)arg;
    const char *name = EVP_CIPHER_get0_name(cipher);
    if (name)
    {
        int key_bits = EVP_CIPHER_get_key_length(cipher) * 8;
        int iv_len = EVP_CIPHER_get_iv_length(cipher);
        int block_sz = EVP_CIPHER_get_block_size(cipher);

        printf("Cipher: %-30s | Key: %3d bits | IV: %2d bytes | Block: %2d bytes\n", name, key_bits, iv_len, block_sz);
    }
}

static void print_digest(EVP_MD *md, void *arg)
{
    (void)arg;
    const char *name = EVP_MD_get0_name(md);
    if (name)
    {
        int digest_size = EVP_MD_get_size(md);
        int block_size = EVP_MD_get_block_size(md);

        printf("Digest: %-30s | Size: %2d bytes | Block: %2d bytes\n", name, digest_size, block_size);
    }
}

static void print_mac(EVP_MAC *mac, void *arg)
{
    (void)arg;

    const char *name = EVP_MAC_get0_name(mac);
    const char *desc = EVP_MAC_get0_description(mac);

    printf("MAC: %-30s | %s\n",
           name ? name : "unknown",
           desc ? desc : "unknown");
}

static void print_rand(EVP_RAND *rand, void *arg)
{
    (void)arg;
    const char *name = EVP_RAND_get0_name(rand);
    const char *desc = EVP_RAND_get0_description(rand);

    printf("RAND: %-30s | %s\n",
           name ? name : "unknown",
           desc ? desc : "unknown");
}

static void print_kdf(EVP_KDF *kdf, void *arg)
{
    (void)arg;
    const char *name = EVP_KDF_get0_name(kdf);
    const char *desc = EVP_KDF_get0_description(kdf);

    printf("KDF: %-30s | %s\n",
           name ? name : "unknown",
           desc ? desc : "unknown");
}

static void print_keymgmt(EVP_KEYMGMT *keymgmt, void *arg)
{
    (void)arg;

    const char *name = EVP_KEYMGMT_get0_name(keymgmt);

    printf("KEYMGMT: %-30s\n", name ? name : "unknown");
}

static void print_signature(EVP_SIGNATURE *sig, void *arg)
{
    (void)arg;

    const char *name = EVP_SIGNATURE_get0_name(sig);
    const char *desc = EVP_SIGNATURE_get0_description(sig);

    printf("SIGNATURE: %-30s | %s\n",
           name ? name : "unknown",
           desc ? desc : "unknown");
}

static void print_asym_cipher(EVP_ASYM_CIPHER *cipher, void *arg)
{
    (void)arg;

    const char *name = EVP_ASYM_CIPHER_get0_name(cipher);
    const char *desc = EVP_ASYM_CIPHER_get0_description(cipher);

    printf("ASYM_CIPHER: %-30s | %s\n",
           name ? name : "unknown",
           desc ? desc : "unknown");
}

static void print_kem(EVP_KEM *kem, void *arg)
{
    (void)arg;

    const char *name = EVP_KEM_get0_name(kem);
    const char *desc = EVP_KEM_get0_description(kem);

    printf("KEM: %-30s | %s\n",
           name ? name : "unknown",
           desc ? desc : "unknown");
}

static void print_keyexch(EVP_KEYEXCH *keyexch, void *arg)
{
    (void)arg;

    const char *name = EVP_KEYEXCH_get0_name(keyexch);
    const char *desc = EVP_KEYEXCH_get0_description(keyexch);

    printf("KEYEXCH: %-30s | %s\n",
           name ? name : "unknown",
           desc ? desc : "unknown");
}

static void print_encoder(OSSL_ENCODER *encoder, void *arg)
{
    (void)arg;
    const char *name = OSSL_ENCODER_get0_name(encoder);
    const char *prop = OSSL_ENCODER_get0_properties(encoder); // 如 "format=der", "structure=private-key"
    printf("ENCODER: %-30s | %s\n", name ? name : "unknown", prop ? prop : "unknown");
}

static void print_decoder(OSSL_DECODER *decoder, void *arg)
{
    (void)arg;
    const char *name = OSSL_DECODER_get0_name(decoder);
    const char *prop = OSSL_DECODER_get0_properties(decoder);
    printf("DECODER: %-30s | %s\n", name ? name : "unknown", prop ? prop : "unknown");
}

static void print_store_loader(OSSL_STORE_LOADER *loader, void *arg)
{
    (void)arg;
    const char *desc = OSSL_STORE_LOADER_get0_description(loader);
    const char *prop = OSSL_STORE_LOADER_get0_properties(loader);
    printf("STORE LOADER: %-30s | %s\n", desc ? desc : "unknown", prop ? prop : "unknown");
}

int main(void)
{
    // export OPENSSL_CONF=~/cpunchline-hub/tools/pre_built/lib/ssl/openssl.cnf
    setenv("OPENSSL_CONF", OPENSSL_CONF_ABSPATH, 1);
    OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL);
    OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL);

    // default OPENSSL_INIT_SETTINGS
    char *config_file = CONF_get1_default_config_file();
    printf("%s\n", config_file);
    OPENSSL_free(config_file);

    printf("[%s]-[%lu]\n", OpenSSL_version(OPENSSL_VERSION), OpenSSL_version_num());
    printf("%s\n", OpenSSL_version(OPENSSL_BUILT_ON));
    printf("%s\n", OpenSSL_version(OPENSSL_PLATFORM));
    printf("%s\n", OpenSSL_version(OPENSSL_CFLAGS));

    // default OSSL_LIB_CTX(use NULL)
    // OSSL_LIB_CTX *default_golbal_default = OSSL_LIB_CTX_get0_global_default();

    OSSL_PROVIDER_do_all(NULL, print_provider, NULL);     // openssl list -providers
    EVP_CIPHER_do_all_provided(NULL, print_cipher, NULL); // openssl list -cipher-algorithms
    EVP_MD_do_all_provided(NULL, print_digest, NULL);     // openssl list -digest-algorithms
    EVP_MAC_do_all_provided(NULL, print_mac, NULL);       // openssl list -mac-algorithms
    EVP_RAND_do_all_provided(NULL, print_rand, NULL);     // openssl list -rand-algorithms
    EVP_KDF_do_all_provided(NULL, print_kdf, NULL);       // openssl list -kdf-algorithms

    // openssl list -public-key-algorithms
    EVP_KEYMGMT_do_all_provided(NULL, print_keymgmt, NULL);         // openssl list -key-managers
    EVP_SIGNATURE_do_all_provided(NULL, print_signature, NULL);     // openssl list -signature-algorithms
    EVP_ASYM_CIPHER_do_all_provided(NULL, print_asym_cipher, NULL); // openssl list -asymcipher-algorithms
    EVP_KEM_do_all_provided(NULL, print_kem, NULL);                 // openssl list -kem-algorithms
    EVP_KEYEXCH_do_all_provided(NULL, print_keyexch, NULL);         // openssl list -key-exchange-algorithms

    OSSL_ENCODER_do_all_provided(NULL, print_encoder, NULL);           // openssl list -encoders
    OSSL_DECODER_do_all_provided(NULL, print_decoder, NULL);           // openssl list -decoders
    OSSL_STORE_LOADER_do_all_provided(NULL, print_store_loader, NULL); // openssl list -store-loaders

    return 0;
}